﻿using System;
using System.Collections.Generic;
using com.yoozoo.gta.Art.RpgScene;
using FluffyUnderware.DevTools;
using Gameplay.PVE.Utils;
using UnityEngine;
using UnityEngine.AI;
using Yoozoo.Gameplay.RTS;
using Yoozoo.Gta.Common;
using Yoozoo.Managers;

namespace Gameplay.PVE.Survivor
{
    public class TeamGroup
    {
        private NavMeshAgent agent;

        public int Count => members.Count;
        
        private Vector3 position;
        

        public Vector3 Position
        {
            set
            {
                position = value;
                anchorPointTransform.position = position;
            }
            get
            {
                return position;
            }
        }

        private Vector3 forward;

        public Vector3 Forward
        {
            get
            {
                return forward;
            }
            set
            {
                forward = value;
                teamRotation = Quaternion.AngleAxis(PveUtils.GetAngle(Vector3.forward, forward), Vector3.up);
            }
        }

        public Transform Transform => anchorPointTransform;
        
        private Dictionary<int,BattleUnit> members = new Dictionary<int,BattleUnit>();

        private GameObject anchorPoint;
        private Transform anchorPointTransform;

        private float moveSpeed = 1.5f;
        
        private GameObject agentObject;

        private NavMeshPath path;
        private List<GameObject> pathDebugList = new List<GameObject>();

        private BattleUnit mainUnit;

        private HudTeam hud;
        public Collider Collider;
        private Transform colliderTransform;

        private int navmeshMask;

        private float teamRadius = 1.2f;

        private BattleGameObject torchObject;

        private Quaternion teamRotation = Quaternion.identity;
        
        public void Initialize()
        {
            Forward = Vector3.forward;
            path = new NavMeshPath();
            anchorPoint = new GameObject("TeamGroup");
            anchorPointTransform = anchorPoint.transform;
            var colliderObject = new GameObject("Collider");
            colliderObject.transform.parent = anchorPointTransform;
            colliderObject.transform.localPosition = Vector3.zero;
            colliderObject.layer = LayerMask.NameToLayer("Ragdoll");
            var collider = colliderObject.AddComponent<SphereCollider>();
            collider.radius = teamRadius;
            Collider = collider;
            colliderTransform = colliderObject.transform;
            var rigidbody = colliderObject.AddComponent<Rigidbody>();
            rigidbody.useGravity = false;
            rigidbody.isKinematic = false;
            //var cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
            //cube.transform.parent = anchorPointTransform;
            anchorPointTransform.parent = SurvivorManager.Instance.Root;
            var circle = BattlePoolManager.Instance.Get(EBattleResId.TeamCircle);
            circle.Parent = anchorPointTransform;
            circle.LocalPosition = Vector3.zero;
            circle.Scale = new Vector3(0.15f, 0.15f, 0.15f);
            /*agentObject = new GameObject("TeamAgent");
            agent = agentObject.AddComponent<NavMeshAgent>();
            agent.acceleration = 999;
            agent.updatePosition = false;
            agent.angularSpeed = 0;
            agent.obstacleAvoidanceType = ObstacleAvoidanceType.NoObstacleAvoidance;
            agent.avoidancePriority = 0;
            agent.autoBraking = false;*/
            hud = ClassManager.Get<HudTeam>();
            hud.Initialize(this);
        }

        public Vector3 GetTeamPosition(int index)
        {
            return position + teamRotation * BattleConstValue.GetTeamPosition(index,members.Count);
        }
        
        public void ResetPosition()
        {
            foreach (var item in members)
            {
                var unit = item.Value;
                var position = GetTeamPosition(item.Key);
                if (MapManager.Instance.IsMapReady)
                {
                    position = BattleHelper.GetNavmeshValidPosition(position);
                }
                unit.Position = position;
                unit.StopMove();
                for (int i = 0; i < unit.summonUnits.Count; i++)
                {
                    unit.summonUnits[i].Position = unit.summonUnits[i].GetSummonPosition();
                    unit.summonUnits[i].StopMove();
                }
            }
        }

        public void ResetTargetPosition()
        {
            foreach (var item in members)
            {
                item.Value.StopMove();
            }
        }

        private int GetMemberIndex()
        {
            for (int i = 0; i < 20; i++)
            {
                if (!members.ContainsKey(i))
                {
                    return i;
                }
            }
            return -1;
        }

        public BattleUnit CreateUnit(int heroId, int index = -1, bool resetPosition = true)
        {
            var battleUnit = UnitManager.Instance.CreateBattleUnit(0, heroId);
            battleUnit.indexInTeam = index;
            AddMember(battleUnit, resetPosition);
            return battleUnit;
            //agent.speed = moveSpeed;
        }

        public void AddMember(BattleUnit battleUnit,bool resetPosition = true,bool insertToFront = false)
        {
            if (battleUnit.indexInTeam == -1)
            {
                battleUnit.indexInTeam = GetMemberIndex();
            }

            if (insertToFront)
            {
                /*var oldMembers = new List<BattleUnit>();
                foreach (var item in members)
                {
                    oldMembers.Add(item.Value);
                    
                }*/
            }
            members.Add(battleUnit.indexInTeam,battleUnit);
            if (battleUnit.GetType() != typeof(NpcUnit))
            {
                StatisticManager.Instance.AddHero(battleUnit);
            }

            PickMainUnit();
            if (resetPosition)
            {
                battleUnit.Position = BattleHelper.GetNavmeshValidPosition(GetTeamPosition(battleUnit.indexInTeam));
                battleUnit.Forward = forward;
            }
            else
            {
                if (!DeckManager.Instance.IsInDeck)
                {
                    battleUnit.TargetPosition = BattleHelper.GetNavmeshValidPosition(GetTeamPosition(battleUnit.indexInTeam));
                }
                //battleUnit.SetAgentTargetPosition(battleUnit.TargetPosition);
            }

            var speed = 0f;
            foreach (var item in members.Values)
            {
                speed += item.moveSpeed;
            }
            moveSpeed = speed / members.Count;
            battleUnit.teamGroup = this;
        }

        public bool IsInBattle()
        {
            var time = TimeManager.time;
            foreach(var member in members.Values)
            {
                var target = member.target;
                if (target != null && !target.isDead)
                {
                    return true;
                }

                if (time - member.lastHitTime < 0.5f)
                {
                    return true;
                }
            }
            return false;
        }

        public void Update()
        {
            BattleUnit defaultTarget = null;
            foreach (var unit in members.Values)
            {
                if (unit.target != null)
                {
                    defaultTarget = unit.target;
                    break;
                }
            }
            if (defaultTarget != null && defaultTarget.team != 0)
            {
                foreach (var unit in members.Values)
                {
                    if (unit.target == null && !unit.isAiming)
                    {
                        unit.teamTarget = defaultTarget;
                        unit.StartAim(defaultTarget);
                    }
                }
            }

            if (isInteracting)
            {
                /*if (lastPosition != interactingUnit.position)
                {
                    var diff = interactingUnit.position - lastPosition;
                    Move(diff,true);
                }*/
            }

            if (guideCompass != null)
            {
                var forward = compassTargetPosition - position;
                forward.y = 0;
                guideCompass.Forward = forward.normalized;
            }

            if (torchObject != null)
            {
                if (mainUnit != null)
                {
                    torchObject.Forward = mainUnit.AimForward;
                }
            }
        }
        
        public void LateUpdate()
        {
            /*var averagePosition = Vector3.zero;
            for (int i = 0; i < memberList.Count; i++)
            {
                averagePosition += memberList[i].Position;
            }
            averagePosition /= memberList.Count;
            Position = averagePosition;*/
            //var startPosition = position;
            //position = anchorPointTransform.position;
            //position.y = 0;
            //var velocity = startPosition - position;
            if (mainUnit != null && !mainUnit.canManualMoving)
            {
                position = mainUnit.Position;
                anchorPointTransform.position = position;
            }

            /*Transform transform = null;
            CollisionManager.Instance.GetRayCastTarget(mainUnit.Position + new Vector3(0,1,0),mainUnit.Forward,5,ref transform);
            if (transform != null)
            {
                BattleDebug.LogError(transform.name);
            }*/
            if (colliderTransform.localPosition != Vector3.zero)
            {
                /*if (isInAirWall)
                {
                    position = colliderTransform.position;
                    position.y = 0;
                    anchorPointTransform.position = position;
                }*/
                colliderTransform.localPosition = Vector3.zero;
            }
        }

        private bool isInAirWall;
        public void SetIsInAirWall(bool isInAirWall)
        {
            this.isInAirWall = isInAirWall;
        }


        private void UpdatePathDebugList()
        {
            for (int i = 0; i < path.corners.Length; i++)
            {
                if (i >= pathDebugList.Count)
                {
                    var cube = GameObject.CreatePrimitive(PrimitiveType.Sphere);
                    cube.transform.parent = SurvivorManager.Instance.Root;
                    cube.transform.localScale = new Vector3(0.1f,0.1f,0.1f);
                    pathDebugList.Add(cube);
                }
                pathDebugList[i].SetActive(true);
                pathDebugList[i].transform.position = path.corners[i];
            }
            for (int i = path.corners.Length; i < pathDebugList.Count; i++)
            {
                pathDebugList[i].SetActive(false);
            }
        }
        
        private Vector3 lastDirection;
        private Vector3 lastPosition;

        public void Move(Vector3 direction, bool fromInteraction = false)
        {
            if (!SurvivorManager.isNavMesh)
            {
                foreach (var unit in members.Values)
                {
                    unit.isManualMoving = true;
                    unit.manualMovingDirection = direction;
                    unit.Position += direction * moveSpeed * Time.deltaTime;
                    if (unit.IsMainUnit)
                    {
                        anchorPointTransform.position = position;
                    }
                }
                return;
            }
            if (!MapManager.Instance.IsMapReady)
            {
                return;
            }
            if (isInteracting && !fromInteraction)
            {
                foreach (var unit in members.Values)
                {
                    if (unit.target == null && !unit.isAiming)
                    {
                        unit.isManualMoving = false;
                        unit.manualMovingDirection = Vector3.zero;
                    }
                }
                return;
            }
            if (direction == Vector3.zero)
            {
                return;
            }

            if (mainUnit != null)
            {
                moveSpeed = mainUnit.moveSpeed;
                if (!mainUnit.CanMove)
                {
                    return;
                }
            }
            direction = ProcessDirection(direction);
            var frameLength = moveSpeed * Time.deltaTime;
            var offset = direction * frameLength;
            var targetPosition = position + offset;
            if (!NavMesh.CalculatePath(position, targetPosition, NavMesh.AllAreas, path))
            {
                if (NavMesh.SamplePosition(targetPosition, out var hit, 2, NavMesh.AllAreas))
                {
                    targetPosition = hit.position;
                    var result = NavMesh.CalculatePath(position, targetPosition, NavMesh.AllAreas, path);
                    if (result)
                    {
                        BattleHelper.GetNavmeshPathPoint(path, frameLength,ref position);
                    }
                }
            }
            else
            {
                //沿着路点走路
                BattleHelper.GetNavmeshPathPoint(path, frameLength,ref position);
            }
            lastDirection = direction;
            foreach (var unit in members.Values)
            {
                unit.isManualMoving = true;
                if (unit == mainUnit)
                {
                    unit.TargetPosition = position;
                }
                else
                {
                    targetPosition = GetTeamPosition(unit.indexInTeam);
                    var maxDistance = 5f;
                    var distance = Vector3.Distance(unit.position, targetPosition);
                    if (distance > maxDistance * 3)
                    {
                        unit.Position = targetPosition;
                    }
                    else if (distance > maxDistance)
                    {
                        maxDistance = -1f;
                    }
                    var pos = BattleHelper.MoveByNavMesh(unit.position, targetPosition, unit.moveSpeed,maxDistance);
                    if (pos == BattleConstValue.DEFAULT_POINT)
                    {
                        pos = BattleHelper.MoveByNavMesh(unit.position, this.position, unit.moveSpeed,maxDistance);
                    }
                    unit.TargetPosition = pos;
                }
                /*else
                {
                    targetPosition = GetTeamPosition(unit.indexInTeam);
                    if (NavMesh.Raycast(position, targetPosition, out var hit, NavMesh.AllAreas))
                    {
                        targetPosition = hit.position;
                    }

                    if (!unit.canManualMoving)
                    {
                        unit.cachedTargetPosition = targetPosition;
                        continue;
                    }
                    unit.TargetPosition = targetPosition;
                    unit.SetAgentTargetPosition(unit.TargetPosition);
                }*/
                unit.manualMovingDirection = direction;
            }
            anchorPointTransform.position = position;
            if (compassTargetPosition != BattleConstValue.DEFAULT_POINT)
            {
                ShowGuideLine();
            }
            ResidentHandler.Inst.GetFunction("L_SurvivorManager.OnTeamMove").Action();
            //UpdatePathDebugList();
            //agent.transform.position = new Vector3(-999,0,-999);
        }

        public void StopMove(bool clearTargetPosition = false)
        {
            if (!SurvivorManager.isNavMesh)
            {
                return;
            }
            if (clearTargetPosition)
            {
                foreach (var unit in members.Values)
                {
                    unit.TargetPosition = default;
                }
            }
            if (lastDirection == Vector3.zero)
            {
                return;
            }
            
            var frameLength = moveSpeed * Time.deltaTime;
            var offset = lastDirection * frameLength;
            foreach (var unit in members.Values)
            {
                unit.isManualMoving = false;
                if (unit == mainUnit)
                {
                    if (!clearTargetPosition)
                    {
                        unit.TargetPosition = position;
                    }
                }
                else
                {
                    var targetPosition = GetTeamPosition(unit.indexInTeam);
                    var pos = unit.position;
                    if (!NavMesh.CalculatePath(pos, targetPosition, NavMesh.AllAreas, path))
                    {
                        if (NavMesh.SamplePosition(targetPosition, out var hit, 2, NavMesh.AllAreas))
                        {
                            targetPosition = hit.position;
                            var result = NavMesh.CalculatePath(pos, targetPosition, NavMesh.AllAreas, path);
                        }
                    }
                    BattleHelper.GetNavmeshPathPoint(path, frameLength,ref pos);
                    unit.TargetPosition = pos;
                }
                /*else
                {
                    var targetPosition = GetTeamPosition(unit.indexInTeam);
                    if (NavMesh.Raycast(position,targetPosition,out var hit,NavMesh.AllAreas))
                    {
                        targetPosition = hit.position;
                    }
                    if (!unit.canManualMoving)
                    {
                        unit.cachedTargetPosition = targetPosition;
                        continue;
                    }

                    if (!clearTargetPosition)
                    {
                        unit.TargetPosition = targetPosition;
                        unit.SetAgentTargetPosition(unit.TargetPosition);
                    }
                }*/
            }
            lastDirection = Vector3.zero;
        }

        private Vector3 ProcessDirection(Vector3 direction)
        {
            //根据相机朝向，转换成世界坐标下的方向
            var cameraForward = CameraManager.Instance.CameraForward;
            cameraForward.y = 0;
            var a = PveUtils.GetAngle(Vector3.forward, cameraForward);
            direction = Quaternion.AngleAxis(a, Vector3.up) * direction;
            if (Physics.Raycast(position + new Vector3(0,0.5f,0),direction,out var hit,teamRadius,1 << BattleConstValue.SurvivorRoadBlockLayer))
            {
                var originDirection = direction;
                var originMagnitude = direction.magnitude;
                var angle = Vector3.Angle(hit.normal, -direction);
                var toHit = hit.point - position;
                var distanceToHit = toHit.magnitude;
                direction = toHit + hit.normal.normalized * Mathf.Cos(angle * Mathf.Deg2Rad) * distanceToHit;
                direction = direction.normalized * originMagnitude;
                //UnityEngine.Debug.LogError(direction + " " + originDirection);
            }
            return direction;
        }
        private BattleUnit interactingUnit;
        private bool isInteracting = false;
        
        public void StartInteract()
        {
            isInteracting = true;
            /*if (memberList.Count > unitIndex)
            {
                interactingUnit = memberList[unitIndex];
            }
            else
            {
                interactingUnit = mainUnit;
            }*/
            //interactingUnit = mainUnit;
        }

        public void StopInteract()
        {
            isInteracting = false;
        }

        private BattleUnit GetUnitByIndex(int index)
        {
            if (members.TryGetValue(index, out var unit))
            {
                return unit;
            }
            return mainUnit;
        }
        
        public void PlayAnimationInteractable(string actionName,int index = 0)
        {
            GetUnitByIndex(index).Play(actionName);
        }

        public void MoveToInteractable(Vector3 position,Vector3 forward,int index = 0)
        {
            GetUnitByIndex(index).MoveToInteractable(position,forward);
        }
        
        public void StopMoveToInteractable(int index = 0)
        {
            GetUnitByIndex(index).StopInteractByMission();
        }
        
        public void RemoveUnitFromTeam(BattleUnit unit)
        {
            if (members.ContainsKey(unit.indexInTeam))
            {
                members.Remove(unit.indexInTeam);
            }

            if (members.Count == 0)
            {
                //战斗失败
                StageManager.Instance.Lose();
                return;
            }
            PickMainUnit();
        }

        private void PickMainUnit()
        {
            if (members.Count == 0)
            {
                mainUnit = null;
                return;
            }
            foreach (var member in members.Values)
            {
                mainUnit = member;
                break;
            }
            foreach (var member in members.Values)
            {
                if (mainUnit == member)
                {
                    member.SetNavmeshAgentActive(false);
                    member.IsMainUnit = true;
                }
                else
                {
                    member.SetNavmeshAgentActive(false);
                    member.IsMainUnit = false;
                }
            }
        }

        public void ShowInteractingHud(string title)
        {
            hud.Show(title);
        }

        public void UpdateInteractingHud(float restTime, float totalTime)
        {
            hud.UpdateProgress(restTime, totalTime);
        }

        public void HideInteractingHud()
        {
            hud.Hide();
        }
        
        private Vector3 compassTargetPosition = BattleConstValue.DEFAULT_POINT;
        private BattleGameObject guideCompass;

        private List<Vector3> guideCampassList = new List<Vector3>();

        public void ChangeGuideCompassPosition(Vector3 lastPosition, Vector3 newPosition)
        {
            for (int i = 0; i < guideCampassList.Count; i++)
            {
                if (guideCampassList[i] == lastPosition)
                {
                    guideCampassList[i] = newPosition;
                    ShowGuideCompass(guideCampassList[0]);
                    break;
                }
            }
        }
        
        public void AddGuideCompass(Vector3 targetPosition)
        {
            guideCampassList.Add(targetPosition);
            ShowGuideCompass(guideCampassList[0]);
        }

        public void RemoveGuideCompass(Vector3 targetPosition)
        {
            guideCampassList.Remove(targetPosition);
            if (guideCampassList.Count == 0)
            {
                HideGuideCompass();
            }
            else
            {
                ShowGuideCompass(guideCampassList[0]);
            }
        }
        
        public void ShowGuideCompass(Vector3 targetPosition)
        {
            compassTargetPosition = targetPosition;
            if (guideCompass == null)
            {
                guideCompass = BattlePoolManager.Instance.Get(EBattleResId.GuideCompass);
                guideCompass.Parent = anchorPointTransform;
                guideCompass.LocalPosition = new Vector3(0, 0f, 0);
            }
            ShowGuideLine();
        }

        public void HideGuideCompass()
        {
            if (guideCompass != null)
            {
                BattlePoolManager.Instance.Release(guideCompass);
                guideCompass = null;
            }
            compassTargetPosition = BattleConstValue.DEFAULT_POINT;
            HideGuideLine();
        }

        public void SetMpMax()
        {
            foreach(var item in members)
            {
                item.Value.currentMp = item.Value.totalMp;
            }
        }


        public void AddElements(List<int> elements)
        {
            foreach (var item in members.Values)
            {
                if (!item.isDead)
                {
                    foreach (var element in elements)
                    {
                        item.AddElement(element, null, null);
                    }
                }
            }
        }

        public void ResetHeroes()
        {
            var oldMembers = new List<BattleUnit>();
            for (int i = 0; i < members.Count; i++)
            {
                oldMembers.Add(members[i]);
            }
            members.Clear();
            for (int i = 0; i < SurvivorManager.initialHeroes.Count; i++)
            {
                var heroId = SurvivorManager.initialHeroes[i];
                var index = Count;
                if (heroId > 0)
                {
                    var unit = CreateUnit(heroId, index);
                    var pos = DeckManager.Instance.GetDeckPosition(index);
                    if (pos == BattleConstValue.DEFAULT_POINT)
                    {
                        unit.Position = GetTeamPosition(index);
                    }
                    else
                    {
                        unit.Position = pos;
                        unit.TargetPosition = GetTeamPosition(index);
                    }
                    ResidentHandler.Inst.GetFunction("L_SurvivorManager.AddBattleHero").Action(heroId);
                }
            }
            for (int i = 0; i < oldMembers.Count; i++)
            {
                oldMembers[i].indexInTeam = -1;
                oldMembers[i].Dead();
            }
        }
        
        private int guideLineId;
        
        public void ShowGuideLine()
        {
            HideGuideLine();
            if (NavMesh.CalculatePath(position, compassTargetPosition, NavMesh.AllAreas, path))
            {
                guideLineId = GuideLineManager.Instance.ShowLine(path.corners);
            }
            else
            {
                if (NavMesh.SamplePosition(compassTargetPosition + (position - compassTargetPosition).normalized * 0.5f, out var hit, 3f, NavMesh.AllAreas))
                {
                    if (NavMesh.CalculatePath(position, hit.position, NavMesh.AllAreas, path))
                    {
                        guideLineId = GuideLineManager.Instance.ShowLine(path.corners);
                    }
                }
            }
        }

        public void HideGuideLine()
        {
            if (guideLineId > 0)
            {
                GuideLineManager.Instance.HideLine(guideLineId);
                guideLineId = 0;
            }
        }

       
        public void SetIsNight(bool isNight)
        {
            if (isNight)
            {
                ShowTorch();
            }
            else
            {
                HideTorch();
            }
        }
        
        public void ShowTorch()
        {
            if (torchObject == null)
            {
                torchObject = BattlePoolManager.Instance.Get(EBattleResId.Torch);
                torchObject.Parent = anchorPointTransform;
                torchObject.LocalPosition = new Vector3(0, 0f, 0);
            }
        }

        public void HideTorch()
        {
            if (torchObject != null)
            {
                BattlePoolManager.Instance.Release(torchObject);
                torchObject = null;
            }
        }
        
        public void Dispose()
        {
            HideTorch();
            members.Clear();
            HideGuideCompass();
            hud?.Dispose();
            hud = null;
            GameObject.Destroy(anchorPoint);
            GameObject.Destroy(agentObject);
            for (int i = 0; i < pathDebugList.Count; i++)
            {
                GameObject.Destroy(pathDebugList[i]);
            }
            pathDebugList.Clear();
            guideCampassList.Clear();
            guideLineId = 0;
            compassTargetPosition = BattleConstValue.DEFAULT_POINT;
        }
    }
}
